﻿using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using SuperMicroWave.Common;
using SuperMicroWave.Models;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using static SuperMicroWave.Common.ConnectInfo;
using System.Collections.Generic;
using System.Linq;
using SuperMicroWave.Dialogs;
using SuperMicroWave.LogFile;
using System.Windows.Input;

namespace SuperMicroWave.ViewModels
{
    public partial class CurveViewModel : ObservableRecipient, IRecipient<ValueChangedMessage<bool>>
    {
        public CurveViewModel() 
        {
            Thread thread = new Thread(new ThreadStart(UpdateState));
            thread.IsBackground = true;
            thread.Start();

            Thread thread1 = new Thread(new ThreadStart(CurveStart));
            thread1.IsBackground = true;
            thread1.Start();

            Thread thread2 = new Thread(new ThreadStart(ExceptionMonitor));
            thread2.IsBackground = true;
            thread2.Start();

            Thread thread3 = new Thread(new ThreadStart(UpdateSysState));
            thread3.IsBackground = true;
            thread3.Start();

            Task.Run(() =>
            {
                List<HeatFlowModel> heatFlowModels = GlobalValue.methodData.HeatFlowItems.ToList();
                InitSetTempData(heatFlowModels);
            });
        }


        #region 全局变量   
        public PIDBase pidBase = new PIDBase();

        public CancellationTokenSource? autoCleanToken;

        public double lineTime = 0;
        public List<Point> lstT = new List<Point>();
        public List<Point> lstT2 = new List<Point>();
        public List<Point> lstP = new List<Point>();
        public List<Point> lstPow = new List<Point>();
        public List<Point> lstSetT = new List<Point>();

        //升温时压力前一刻读数
        public double plPre = 0;
        //干烧标记
        public bool emptyHeatFlag = false;

        //是否可以绘图
        public bool CanDraw = false;
        //是否是当前系统控制升温
        public bool IsCurControl = false;
        #endregion


        #region 数据源
        [ObservableProperty]
        public IPointDataSource lineT;   
        [ObservableProperty]
        public IPointDataSource lineSetT;
        [ObservableProperty]
        public IPointDataSource lineP;
        [ObservableProperty]
        public IPointDataSource linePow;
        [ObservableProperty]
        public IPointDataSource lineT2;   

        [ObservableProperty]
        public ushort powerSet;

        [ObservableProperty]
        public string heatImg = "../Assets/Images/开始.png";
        [ObservableProperty]
        public string pauseImg = "../Assets/Images/暂停.png";

        [ObservableProperty]
        public double preScale = GlobalValue.sysParams.PreScale;
        [ObservableProperty]
        public double stabScale = GlobalValue.sysParams.StabScale;
        [ObservableProperty]
        public int stabTime = GlobalValue.sysParams.StabTime;
        [ObservableProperty]
        public double leakV = GlobalValue.sysParams.LeakV;

        [ObservableProperty]
        public double autoCleanPreMax = GlobalValue.sysParams.AutoCleanPreMax;
        [ObservableProperty]
        public double autoCleanPreMin = GlobalValue.sysParams.AutoCleanPreMin;
        #endregion


        #region 命令函数
        [RelayCommand]
        public void HeatUp()
        {
            if (!GlobalValue.modBusTcp.IsConnected())
            {
                MessageBox.Show("通信未连接", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
                return;
            }

            if (EventResponseController.Instance.CanExecute() == false)
                return;

            if (GlobalValue.NowState.CurState == InsState.DEV_IDLE)
            {
                LogHelper.WriteInfoLog("点击开始按钮");

                //初始化目标温度曲线
                Task.Run(() =>
                {
                    List<HeatFlowModel> heatFlowModels = GlobalValue.methodData.HeatFlowItems.ToList();
                    InitSetTempData(heatFlowModels, (float)GlobalValue.NowState.T);
                });

                //初始化绘图曲线
                lineTime = 0;
                lstT.Clear();
                lstT2.Clear();
                lstP.Clear();
                lstPow.Clear();

                //正常升温流程
                if(!GlobalValue.NowState.IsAutoClean)
                {
                    //初始化干烧标记
                    emptyHeatFlag = false;

                    InflatingDlg inflatingDlg = new InflatingDlg();
                    if (inflatingDlg.ShowDialog() == false)
                    {
                        LogHelper.WriteInfoLog("退出加压窗口后，关闭进气阀");
                        GlobalValue.modBusTcp.WriteCoil("0", false);  //进气阀
                        return;
                    }

                    LogHelper.WriteInfoLog("开启PIDControl任务");
                    Task.Run(() =>
                    {
                        PIDControl();
                    });
                }
                else //自动清洗流程
                {
                    emptyHeatFlag = true;

                    LogHelper.WriteInfoLog("开启AutoClean任务");
                    autoCleanToken = new CancellationTokenSource();
                    Task.Run(() =>
                    {
                        AutoClean();
                        autoCleanToken.Dispose();
                        autoCleanToken = null;
                    }, autoCleanToken.Token);
                }
            }
            else
            {
                LogHelper.WriteInfoLog("点击结束按钮");
                ResetState();
            }
        }

        [RelayCommand]
        public void Pause()
        {
            if(GlobalValue.NowState.CurState == InsState.DEV_HEAT)
            {
                LogHelper.WriteInfoLog("点击暂停按钮");
                GlobalValue.modBusTcp.WriteRegister("65", 0xff);  //暂停
            }
            else if(GlobalValue.NowState.CurState == InsState.DEV_PAUSE)
            {
                LogHelper.WriteInfoLog("点击继续升温按钮");
                GlobalValue.modBusTcp.WriteRegister("65", 1);  //继续升温
            }
        }

        [RelayCommand]
        public async Task SaveTable()
        {
            if(GlobalValue.NowState.CurState != InsState.DEV_IDLE)
            {
                MessageBox.Show("请等待流程结束");
                return;
            }
            if(lstT.Count == 0 || lstT2.Count == 0)
            {
                MessageBox.Show("数据为空");
                return;
            }

            await Task.Run(() =>
            {
                int nCount = 0;
                int TopIndex = 0;
                double maxValue = lstT[0].Y;
                for(int i = 0; i < lstT.Count; i++)
                {
                    if (lstT[i].Y >= maxValue)
                    {
                        TopIndex = i;
                        maxValue = lstT[i].Y;
                        nCount = 0;
                    }
                    else
                        nCount++;

                    if (nCount == 200)
                        break;
                }

                List<double> lHeatKey = new List<double>();
                List<double> lHeatValue = new List<double>();
                for (int i = 0; i <= TopIndex; i++)
                {
                    lHeatKey.Add(Math.Round(lstT2[i].Y, 1));
                    lHeatValue.Add(Math.Round(lstT[i].Y, 1));
                }

                List<double> lCoolKey = new List<double>();
                List<double> lCoolValue = new List<double>();
                for (int i = TopIndex + 1; i < lstT.Count; i++)
                {
                    lCoolKey.Add(Math.Round(lstT2[i].Y, 1));
                    lCoolValue.Add(Math.Round(lstT[i].Y, 1));
                }

                GlobalValue.HeatTable.SaveHashTable(lHeatKey, lHeatValue);
                GlobalValue.CoolTable.SaveHashTable(lCoolKey, lCoolValue);
                MessageBox.Show("构建成功");
            });
        }

        [RelayCommand]
        public void ClearTable()
        {
            GlobalValue.HeatTable.ClearValue();
            GlobalValue.CoolTable.ClearValue();
            MessageBox.Show("清除成功");
        }
        #endregion


        #region 功能函数
        /// <summary>
        /// 初始化设置温度的数据
        /// </summary>
        public void InitSetTempData(List<HeatFlowModel> heatFlowModels, float startTemp = 0)
        {
            try
            {
                double lineTime = 0;
                lstSetT.Clear();
     
                for (int i = 0; i < heatFlowModels.Count; i++)
                {
                    HeatFlowModel hfm = heatFlowModels[i];

                    // 升温阶段
                    double steps = hfm.HeatTime * 60 / 3;
                    if (i != 0)
                    {
                        HeatFlowModel last = heatFlowModels[i - 1];
                        startTemp = last.TargetTemp;
                    }
                    double stepTemp = (hfm.TargetTemp - startTemp) / steps;

                    for (int j = 0; j < steps; j++)
                    {
                        Point pt = new Point(lineTime, startTemp + stepTemp * j);
                        lstSetT.Add(pt);
             
                        lineTime += 0.05;
                    }
                }
                LineSetT = GetPoint(lstSetT);
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message, "");
            }
        }

        public async void PIDControl()
        {
            int addr = 15;  //升温曲线节点起始地址
            ushort curTime = 0;  //升温曲线当前时间点

            int count = GlobalValue.methodData.HeatFlowItems.Count;
            count = count > 10 ? 10 : count;   //最多存10条升温流程
            for (int i = 0; i < count; i++)
            {
                HeatFlowModel hfm = GlobalValue.methodData.HeatFlowItems[i];

                if(hfm.HeatTime > 0)
                {
                    curTime += (ushort)(hfm.HeatTime * 60);
                    GlobalValue.modBusTcp.WriteRegister(addr++.ToString(), curTime);
                    GlobalValue.modBusTcp.WriteRegister(addr++.ToString(), (ushort)(hfm.TargetTemp * 100));
                    GlobalValue.modBusTcp.WriteRegister(addr++.ToString(), (ushort)hfm.P);
                    GlobalValue.modBusTcp.WriteRegister(addr++.ToString(), (ushort)hfm.I);
                    GlobalValue.modBusTcp.WriteRegister(addr++.ToString(), (ushort)hfm.D);
                }        
            }
            GlobalValue.modBusTcp.WriteRegister(addr++.ToString(), 0);

            IsCurControl = true;  //先置为true再升温，防止程序读取下位机温度曲线
            GlobalValue.modBusTcp.WriteRegister("65", 1);  //开始升温
            LogHelper.WriteInfoLog("升温流程下发完毕，开始升温");

            //阀控制
            GlobalValue.modBusTcp.WriteCoil("0", false);  //进气阀
            GlobalValue.modBusTcp.WriteCoil("1", false);  //排气阀
            GlobalValue.modBusTcp.WriteCoil("2", true);   //冷水阀
            GlobalValue.modBusTcp.WriteCoil("3", true);   //冷水机
            GlobalValue.modBusTcp.WriteCoil("4", false);  //换气扇
            LogHelper.WriteInfoLog("进气阀关闭，排气阀关闭，冷水阀打开，冷水机打开，排气扇关闭");

            await Task.Delay(100);
            CanDraw = true;  //等待仪器为升温状态，开始绘图
        }

        /// <summary>
        /// 自动清洗
        /// </summary>
        public void AutoClean()
        {
            PIDControl();

            for (int i = 0; i < 4; i++)
            {
                if(autoCleanToken!.IsCancellationRequested)
                {
                    LogHelper.WriteInfoLog("AutoClean 任务取消");
                    return;
                }

                bool flag = true;  //是否可以排气
                while(true)
                {
                    if (autoCleanToken!.IsCancellationRequested)
                    {
                        LogHelper.WriteInfoLog("AutoClean 任务取消");
                        return;
                    }

                    if (flag && lstP.Count != 0 && lstP.Last().Y >= AutoCleanPreMax)
                    {
                        LogHelper.WriteInfoLog($"压力：{lstP.Last().Y} >= {AutoCleanPreMax}，打开排气阀，关闭功率输出");
                        GlobalValue.modBusTcp.WriteCoil("1", true);  //打开排气阀
                        flag = false;
                    }
                    if(!flag && lstP.Count != 0 && lstP.Last().Y <= AutoCleanPreMin)
                    {
                        LogHelper.WriteInfoLog($"压力：{lstP.Last().Y} <= {AutoCleanPreMin}，关闭排气阀，满功率输出");
                        GlobalValue.modBusTcp.WriteCoil("1", false);  //关闭排气阀
                        break;
                    }
                    Thread.Sleep(2000);
                }
            }
            LogHelper.WriteInfoLog("自动清洗完毕，关闭冷水阀，开始降温");
            GlobalValue.modBusTcp.WriteRegister("65", 0);  //停止自动清洗
        }

        /// <summary>
        /// 升温曲线
        /// </summary>
        public void CurveStart()
        {
            while (true)
            {
                if (!GlobalValue.modBusTcp.IsConnected())
                {
                    Thread.Sleep(1000);
                    continue;
                }

                if (GlobalValue.NowState.CurState == InsState.DEV_IDLE && !CanDraw)
                {
                    Thread.Sleep(1000);
                    continue;
                }

                // 根据HashTable获取磁控管温度相对应的管内温度
                double tempT = GlobalValue.NowState.T;
                //if(GlobalValue.NowState.CurState == InsState.DEV_HEAT)
                //{
                //    tempT = GlobalValue.HeatTable.GetValue(tempT);
                //}
                //else if(GlobalValue.NowState.CurState == InsState.DEV_COOL_DOWN)
                //{
                //    tempT = GlobalValue.CoolTable.GetValue(tempT);
                //}

                if (GlobalValue.NowState.CurState == InsState.DEV_IDLE && tempT <= GlobalValue.methodData.EndTemp)
                {
                    CanDraw = false;
                    LogHelper.WriteInfoLog($"降温状态且{tempT} < {GlobalValue.methodData.EndTemp}，降温结束，打开排气阀，关闭冷水机");
                    GlobalValue.modBusTcp.WriteCoil("1", true);   //打开排气阀
                    GlobalValue.modBusTcp.WriteCoil("4", true);   //打开排气扇
                    GlobalValue.modBusTcp.WriteCoil("3", false);   //关闭冷水机
                    continue;
                }
                Point pt = new Point(lineTime, tempT);
                lstT.Add(pt);
                LineT = GetPoint(lstT);

                double tempP = GlobalValue.NowState.P1;
                Point pt1 = new Point(lineTime, tempP);
                lstP.Add(pt1);
                LineP = GetPoint(lstP);

                double tempPow = (double)GlobalValue.hRegData.R0 / 3600 * 100;
                Point pt2 = new Point(lineTime, tempPow);
                lstPow.Add(pt2);
                LinePow = GetPoint(lstPow);

                double tempT2 = GlobalValue.NowState.T1;
                Point pt3 = new Point(lineTime, tempT2);
                lstT2.Add(pt3);
                LineT2 = GetPoint(lstT2);

                lineTime += 0.05;             
                Thread.Sleep(3000);
            }
        }

        /// <summary>
        /// 复位为空闲状态
        /// </summary>
        public void ResetState()
        {
            CanDraw = false;
            // 正常升温
            if (!GlobalValue.NowState.IsAutoClean)
            {
                LogHelper.WriteInfoLog("停止升温");
                GlobalValue.modBusTcp.WriteRegister("65", 0);
            }
            else // 自动清洗
            {
                LogHelper.WriteInfoLog("停止自动清洗");
                autoCleanToken?.Cancel();
                GlobalValue.modBusTcp.WriteRegister("65", 0);
            }
        }

        /// <summary>
        /// 异常监测
        /// </summary>
        public void ExceptionMonitor()
        {
            while (true)
            {
                if (GlobalValue.modBusTcp.IsConnected())
                {
                    //if (GlobalValue.NowState.T1 > 80)   //磁控管温度过高，切换至待机状态
                    //{
                    //    if (GlobalValue.NowState.CurState == InsState.DEV_HEAT)
                    //    {
                    //        ResetState();
                    //        MessageBox.Show("磁控管温度过高，已切换至待机状态", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
                    //    }
                    //}

                    if(GlobalValue.NowState.T1 >= 60)
                    {
                        bool[]? value = GlobalValue.modBusTcp.ReadCoils("2", 1);
                        if(value != null && !value[0])
                        {
                            LogHelper.WriteInfoLog("磁控管温度过高：打开冷水阀");
                            GlobalValue.modBusTcp.WriteCoil("2", true);
                        }
                    }

                    if ((GlobalValue.NowState.T / 100) > 305)  //罐内温度过高
                    {
                        if (GlobalValue.NowState.CurState == InsState.DEV_HEAT)
                        {
                            ResetState();
                            if (MessageBox.Show("罐内温度超出限制，是否开启自动冷却？", "提示", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
                            {
                                GlobalValue.modBusTcp.WriteCoil("2", true);  //打开冷水阀
                            }
                        }
                    }

                    //非自动清洗时监测
                    if (GlobalValue.NowState.CurState == InsState.DEV_HEAT && !GlobalValue.NowState.IsAutoClean)
                    {
                        double plCur = GlobalValue.NowState.P1;
                        double tempP = GlobalValue.sysParams.LeakV * 2;
                        if ((plPre - plCur) >= tempP)
                        {
                            Pause();
                            MessageBox.Show($"压力值减小，请检查是否存在泄露({plPre:N2} : {plCur:N2})", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
                            plPre = 0;
                        }
                        else
                        {
                            plPre = plCur;
                        }
                    }
                    else if(plPre != 0)
                    {
                        plPre = 0;
                    }

                    if (GlobalValue.NowState.CurState == InsState.DEV_HEAT && !emptyHeatFlag && 
                        lstPow.Count >= 40 && lstT.Count >= 40 && lstT.Last().Y < 100) //开始实验2分钟时
                    {
                        emptyHeatFlag = true;

                        var lstY = lstPow.Select(p => p.Y).ToList();
                        double totalP = lstY.Take(40).Sum();  
                        double tempT = 5 * totalP / 4000;
                        double realT = lstT[39].Y - lstT[0].Y;
                        if(realT < tempT)
                        {
                            Pause();
                            MessageBox.Show($"检查到升温过慢，请确认仪器是否干烧({realT:N2} : {tempT:N2})", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
                        }
                    }
                }
                Thread.Sleep(2000);
            }           
        }

        /// <summary>
        /// 更新仪器状态信息
        /// </summary> 
        public void UpdateState()
        {
            while (true)
            {               
                if(GlobalValue.modBusTcp.IsConnected())
                {
                    PowerSet = (ushort)(GlobalValue.hRegData.R0 * 0.425);
                    HeatImg = GlobalValue.NowState.CurState == InsState.DEV_IDLE ? "../Assets/Images/开始.png" : "../Assets/Images/结束.png";
                    PauseImg = GlobalValue.NowState.CurState == InsState.DEV_PAUSE ? "../Assets/Images/开始.png" : "../Assets/Images/暂停.png";
                }
                Thread.Sleep(100);
            }
        }

        public void UpdateSysState()
        {
            while (true)
            {
                if (GlobalValue.modBusTcp.IsConnected())
                {
                    InsState curState = GlobalValue.NowState.CurState;
                    if (curState == InsState.DEV_IDLE)
                    {
                        if (GlobalValue.NowState.LastState == InsState.DEV_HEAT)
                        {
                            //若升温已结束，自动清洗流程未关闭，则关闭流程
                            if(GlobalValue.NowState.IsAutoClean)
                            {
                                autoCleanToken?.Cancel();
                            }
                            LogHelper.WriteInfoLog("由升温状态到空闲状态：关闭冷水阀");
                            GlobalValue.modBusTcp.WriteCoil("2", false);
                        }
                        //当系统变为空闲状态时，IsCurControl设置为false
                        if(GlobalValue.NowState.LastState != curState)
                        {
                            LogHelper.WriteInfoLog("系统变为空闲状态：IsCurControl设置为false");
                            IsCurControl = false;
                        }
                    }
                    if (curState == InsState.DEV_HEAT)
                    {
                        //当不是本系统控制升温且为升温状态时，读取下位机的升温曲线
                        if(GlobalValue.NowState.LastState != curState && !IsCurControl)
                        {
                            LogHelper.WriteInfoLog("升温状态且IsCurControl为false：读取下位机的升温曲线");
                            ReadSetTempData();
                        }
                        if(GlobalValue.NowState.LastState == InsState.DEV_IDLE)
                        {
                            LogHelper.WriteInfoLog("由空闲状态到升温状态：打开冷水阀");
                            GlobalValue.modBusTcp.WriteCoil("2", true);
                        }
                    }
                    if (curState == InsState.DEV_PAUSE)
                    {

                    }
                    GlobalValue.NowState.LastState = curState;
                }
                else
                {
                    GlobalValue.NowState.LastState = InsState.DEV_ERROR;
                }
                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// 从下位机读取升温曲线
        /// </summary>
        public void ReadSetTempData()
        {
            List<HeatFlowModel> heatFlowModels = new List<HeatFlowModel>();

            uint curTime = 0;
            ushort[]? allDatas = GlobalValue.modBusTcp.ReadRegister("15", 50);
            if (allDatas == null) return;

            for(int i = 0; i < allDatas.Length; i+=5)
            {
                if (allDatas[i] == 0)
                    break;

                HeatFlowModel model = new HeatFlowModel();
                model.HeatTime = (uint)(allDatas[i] / 60 - curTime);
                curTime += model.HeatTime;
                model.TargetTemp = (uint)(allDatas[i + 1] / 100);

                heatFlowModels.Add(model);
            }

            InitSetTempData(heatFlowModels);
        }

        /// <summary>
        /// 方法改变重置升温曲线
        /// </summary>
        public void Receive(ValueChangedMessage<bool> message)
        {
            Task.Run(() =>
            {
                List<HeatFlowModel> heatFlowModels = GlobalValue.methodData.HeatFlowItems.ToList();
                InitSetTempData(heatFlowModels);
            });           
        }

        public IPointDataSource GetPoint(List<Point> pt)
        {       
            List<Point> pts = new List<Point>(pt);

            var ds = new EnumerableDataSource<Point>(pts);
            ds.SetXYMapping(pt => pt);
            return ds;
        }

        partial void OnPreScaleChanged(double value)
        {
            GlobalValue.sysParams.PreScale = value;
            INIFile.InifileWriteValue("Test", "PreScale", value.ToString(), INIFile.iniPath);
        }
        partial void OnStabScaleChanged(double value)
        {
            GlobalValue.sysParams.StabScale = value;
            INIFile.InifileWriteValue("Test", "StabScale", value.ToString(), INIFile.iniPath);
        }
        partial void OnStabTimeChanged(int value)
        {
            GlobalValue.sysParams.StabTime = value;
            INIFile.InifileWriteValue("Test", "StabTime", value.ToString(), INIFile.iniPath);
        }
        partial void OnLeakVChanged(double value)
        {
            GlobalValue.sysParams.LeakV = value;
            INIFile.InifileWriteValue("Test", "LeakV", value.ToString(), INIFile.iniPath);
        }
        partial void OnAutoCleanPreMaxChanged(double value)
        {
            GlobalValue.sysParams.AutoCleanPreMax = value;
            INIFile.InifileWriteValue("Test", "AutoCleanPreMax", value.ToString(), INIFile.iniPath);
        }
        partial void OnAutoCleanPreMinChanged(double value)
        {
            GlobalValue.sysParams.AutoCleanPreMin = value;
            INIFile.InifileWriteValue("Test", "AutoCleanPreMin", value.ToString(), INIFile.iniPath);
        }
        #endregion
    }
}
